package org.abettor.angleworm.sip;

import gov.nist.javax.sdp.fields.Email;
import gov.nist.javax.sdp.fields.EmailAddress;
import gov.nist.javax.sdp.fields.EmailField;
import gov.nist.javax.sdp.fields.PhoneField;

import java.net.InetAddress;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import javax.sdp.BandWidth;
import javax.sdp.Connection;
import javax.sdp.EMail;
import javax.sdp.Info;
import javax.sdp.MediaDescription;
import javax.sdp.Origin;
import javax.sdp.Phone;
import javax.sdp.SdpConstants;
import javax.sdp.SdpFactory;
import javax.sdp.SessionDescription;
import javax.sdp.SessionName;
import javax.sdp.Version;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

public class SipCaller implements SipListener
{
	private static final String TRANSPORT = "udp";
	private static final int BIND_PORT = 24680;
	private static final int DEST_PORT = 13579;

	private SipStack sipStack;
	private MessageFactory messageFactory;
	private AddressFactory addressFactory;
	private HeaderFactory headerFactory;

	public void call() throws Exception
	{
		Properties prop = new Properties();
		prop.setProperty("javax.sip.IP_ADDRESS", InetAddress.getLocalHost().getHostAddress());
		prop.setProperty("javax.sip.RETRANSMISSION_FILTER", "true");
		prop.setProperty("javax.sip.STACK_NAME", "[org_abettor_angleworm_sip]");
		//	0为产品发布模式；16为跟踪模式；32为跟踪加调试模式
		prop.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
		prop.setProperty("gov.nist.javax.sip.DEBUG_LOG", "log/SipCaller.debug.log");
		prop.setProperty("gov.nist.javax.sip.SERVER_LOG", "log/SipCaller.log");
		prop.setProperty("gov.nist.javax.sip.READ_TIMEOUT", "100");
		prop.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", "false");
		//prop.setProperty("javax.sip.ROUTER_PATH", "examples.shootist.MyRouter");

		SipFactory sipFactory = SipFactory.getInstance();
		sipFactory.setPathName("gov.nist");
		sipStack = sipFactory.createSipStack(prop);
		messageFactory = sipFactory.createMessageFactory();
		addressFactory = sipFactory.createAddressFactory();
		headerFactory = sipFactory.createHeaderFactory();

		//	设置监听点
		ListeningPoint lp = sipStack.createListeningPoint(InetAddress.getLocalHost().getHostAddress(), BIND_PORT, TRANSPORT);
		SipProvider sipProvider = sipStack.createSipProvider(lp);
		sipProvider.addSipListener(this);

		String fromUser = "SipCaller";
		String fromSipAddress = InetAddress.getLocalHost().getHostAddress();
		String fromDisplayName = "The SIP Caller";
		SipURI fromAddress = addressFactory.createSipURI(fromUser, fromSipAddress);
		fromAddress.setPort(BIND_PORT);
		Address fromNameAddress = addressFactory.createAddress(fromAddress);
		fromNameAddress.setDisplayName(fromDisplayName);
		FromHeader fromHeader = headerFactory.createFromHeader(fromNameAddress, fromUser);

		String toUser = "SipCallee";
		String toSipAddress = InetAddress.getLocalHost().getHostAddress();
		String toDisplayName = "The SIP Callee";
		SipURI toAddress = addressFactory.createSipURI(toUser, toSipAddress);
		toAddress.setPort(DEST_PORT);
		Address toNameAddress = addressFactory.createAddress(toAddress);
		toNameAddress.setDisplayName(toDisplayName);
		ToHeader toHeader = headerFactory.createToHeader(toNameAddress, toUser);

		String host = sipStack.getIPAddress();
		List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
		ViaHeader viaHeader = headerFactory.createViaHeader(host, BIND_PORT, TRANSPORT, null);
		viaHeaders.add(viaHeader);

		SipURI requestURI = addressFactory.createSipURI(toUser, toSipAddress);
		requestURI.setPort(DEST_PORT);
		CallIdHeader callIdHeader = sipProvider.getNewCallId();
		CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
		MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(100);
		Request request = messageFactory.createRequest(requestURI, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

		SipURI contactURI = addressFactory.createSipURI(fromUser, host);
		contactURI.setPort(BIND_PORT);
		Address contactAddress = addressFactory.createAddress(contactURI);
		contactAddress.setDisplayName(fromDisplayName);
		ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
		request.addHeader(contactHeader);

		//	SDP载荷
		SdpFactory sdpFactory = SdpFactory.getInstance();
		Version sdpVersion = sdpFactory.createVersion(0);
		Origin sdpOrigin = sdpFactory.createOrigin(fromUser, SdpFactory.getNtpTime(new Date()), SdpFactory.getNtpTime(new Date()), Connection.IN, Connection.IP4, fromSipAddress);
		SessionName sdpSessionName = sdpFactory.createSessionName("SESSION NAME");
		Info sdpInfo = sdpFactory.createInfo("THIS IS THE SESSION INFOMATION");//*
		EmailField sdpEmail = new EmailField();//*
		Email email = new Email();
		email.setUserName("admin");
		email.setHostName("gmail.com");
		EmailAddress emailAddr = new EmailAddress();
		emailAddr.setDisplayName("Admin");
		emailAddr.setEmail(email);
		sdpEmail.setEmailAddress(emailAddr);
		PhoneField sdpPhone = new PhoneField();//*
		sdpPhone.setName("Admin");
		sdpPhone.setPhoneNumber("+86-22-12345678");
		Connection sdpConnection = sdpFactory.createConnection(Connection.IN, Connection.IP4, fromSipAddress);//*
		BandWidth sdpBandWidth = sdpFactory.createBandwidth(BandWidth.AS, 128);//*
		int[] audioFmts = new int[]{SdpConstants.G723, SdpConstants.GSM, SdpConstants.PCMU, SdpConstants.DVI4_8000, SdpConstants.DVI4_16000, SdpConstants.PCMA, SdpConstants.G728};
		MediaDescription sdpAudioMediaDescription = sdpFactory.createMediaDescription("audio", 49170, 2, "RTP/AVP", audioFmts);//*
		int[] videoFmts = new int[]{SdpConstants.H263, SdpConstants.JPEG, SdpConstants.H261};
		MediaDescription sdpVideoMediaDescription = sdpFactory.createMediaDescription("video", 49172, 2, "RTP/AVP", videoFmts);//*
		SessionDescription sessionDescription = sdpFactory.createSessionDescription();
		sessionDescription.setVersion(sdpVersion);
		sessionDescription.setOrigin(sdpOrigin);
		sessionDescription.setSessionName(sdpSessionName);
		sessionDescription.setInfo(sdpInfo);
		Vector<EMail> emails = new Vector<EMail>();
		emails.add(sdpEmail);
		sessionDescription.setEmails(emails);
		Vector<Phone> phones = new Vector<Phone>();
		phones.add(sdpPhone);
		sessionDescription.setPhones(phones);
		sessionDescription.setConnection(sdpConnection);
		Vector<BandWidth> bandWidths = new Vector<BandWidth>();
		bandWidths.add(sdpBandWidth);
		sessionDescription.setBandwidths(bandWidths);
		Vector<MediaDescription> mediaDescriptions = new Vector<MediaDescription>();
		mediaDescriptions.add(sdpAudioMediaDescription);
		mediaDescriptions.add(sdpVideoMediaDescription);
		sessionDescription.setMediaDescriptions(mediaDescriptions);
		ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp");
		request.setContent(sessionDescription, contentTypeHeader);

		ClientTransaction clientTransaction = sipProvider.getNewClientTransaction(request);
		clientTransaction.sendRequest();
	}

	@Override
	public void processRequest(RequestEvent e)
	{
		Request request = e.getRequest();
		ServerTransaction serverTransaction = e.getServerTransaction();
		System.out.println("Request " + request.getMethod() + " received at " + sipStack.getStackName() + " with server transaction id " + serverTransaction);

		if (request.getMethod().equals(Request.BYE))
		{
			try
			{
				processBye(request, serverTransaction);
			}
			catch (ParseException ex)
			{
				ex.printStackTrace();
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
			catch (InvalidArgumentException ex)
			{
				ex.printStackTrace();
			}
		}
	}

	public void processBye(Request request, ServerTransaction serverTransaction) throws ParseException, SipException, InvalidArgumentException
	{
		if(serverTransaction != null)
		{
			Response response = messageFactory.createResponse(Response.OK, request);
			serverTransaction.sendResponse(response);
		}
	}

	@Override
	public void processResponse(ResponseEvent e)
	{
		Response response = e.getResponse();
		ClientTransaction clientTransaction = e.getClientTransaction();
		System.out.println("Response received with client transaction id " + clientTransaction + ":\n" + response);

		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
		if(response.getStatusCode() == Response.OK && cseqHeader.getMethod().equals(Request.INVITE))
		{
			Dialog dialog = clientTransaction.getDialog();
			try
			{
				Request request = dialog.createAck(cseqHeader.getSeqNumber());
				dialog.sendAck(request);
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
			catch(InvalidArgumentException ex)
			{
				ex.printStackTrace();
			}
		}
	}

	@Override
	public void processTimeout(TimeoutEvent e)
	{
		System.out.println("Transaction time out.");
	}

	@Override
	public void processTransactionTerminated(TransactionTerminatedEvent e)
	{
		System.out.println("Transaction terminated");
	}

	@Override
	public void processDialogTerminated(DialogTerminatedEvent e)
	{
		System.out.println("Dialog terminated");
	}

	@Override
	public void processIOException(IOExceptionEvent e)
	{
		System.out.println("IOException thrown.");
	}

	public static void main(String[] args) throws Exception
	{
		new SipCaller().call();
	}
}
